package com.fingard.dsp.bank.directbank.abc23.util;

public class SimpleDESHelper_Char {
    public static final byte ENCRYPT = 0;
    public static final byte DECRYPT = 1;
    /**
     * 值使用多少个字节表示
     */
    public static final int BYTE_COUNT = 5;
    /**
     * 3, 5, 2, 7, 4, 10, 1, 9, 8, 6
     */
    private static byte[] P_10 = new byte[] { 3, 5, 2, 7, 4, 10, 1, 9, 8, 6 };
    /**
     * 6, 3, 7, 4, 8, 5, 10, 9
     */
    private static byte[] P_8 = { 6, 3, 7, 4, 8, 5, 10, 9 };
    /**
     * 2, 6, 3, 1, 4, 8, 5, 7
     */
    private static byte[] IP = { 2, 6, 3, 1, 4, 8, 5, 7 };
    /**
     * 4, 1, 3, 5, 7, 2, 8, 6
     */
    private static byte[] FP = { 4, 1, 3, 5, 7, 2, 8, 6 };
    /**
     * 4, 1, 2, 3, 2, 3, 4, 1
     */
    private static byte[] E_P = { 4, 1, 2, 3, 2, 3, 4, 1 };
    /**
     * 1, 0, 3, 2, 
     * 3, 2, 1, 0, 
     * 0, 2, 1, 3, 
     * 3, 1, 3, 2
     */
    private byte[][] S0 = new byte[][]{
              {1, 0, 3, 2}, 
              {3, 2, 1, 0}, 
              {0, 2, 1, 3}, 
              {3, 1, 3, 2}
                  };
    /**
     * 0, 1, 2, 3, 
     * 2, 0, 1, 3,
     * 3, 0, 1, 0, 
     * 2, 1, 0, 3
     */
    private byte[][] S1 = {
              {0, 1, 2, 3}, 
              {2, 0, 1, 3}, 
              {3, 0, 1, 0}, 
              {2, 1, 0, 3}
                  };
    /**
     * 2, 4, 3, 1
     */
    private byte[] P_4 = { 2, 4, 3, 1 };


    //byte[] bKey = new byte[16];
    private byte[] bPlain = new byte[8];
    /**
     * 将 byte 的左边第 n 个 bit 与第 8-n 个 bit 交换位置
     * @param X
     * @param n
     * @return
     */
    private byte crotl(byte X, byte n)
    {
        // Rotate Left n 
        return (byte)(X << n | X >> (8 - n));
    }
    /**
     * 将 byte 的右边第 n 个 bit 与第 8-n 个 bit 交换位置
     * @param X
     * @param n
     * @return
     */
    private byte crotr(byte X, byte n)
    {
        // Rotate Right n 
        return(byte)(X >> n | X << (8 - n));
    }

    private void byte2bit(char b, byte[] bitArray)
    {
        //循环获取一个 byte 中的每一个 bit
        //顺序按照从左往右获取
        //每个 bit 信息按照顺序保存到 bitArray 中。
        for (int i = 0; i < 8; i++)
        {
            //每次取出 1 个 bit 的信息
            bitArray[i] = (byte)(b >> (7 - i) & (0x01));
        }
    }
    /**
     * 将给定 byte 的每一个 bit 信息，获取到 bit 数组中
     * @param byteArray
     * @param bitArray
     * @param byteCount
     */
    private void byte2bit(char[] byteArray, byte[] bitArray, int byteCount)
    {
        int bitIndex = 0;
        for (int i = 0; i < byteCount; i++)
        {
            //循环获取一个 byte 中的每一个 bit
            //顺序按照从左往右获取
            //每个 bit 信息按照顺序保存到 bitArray 中。
            for (int j = 0; j < 8; j++)
            {
                //每次取出 1 个 bit 的信息
                bitArray[bitIndex] = (byte)(byteArray[i] >> (7 - j) & (0x01));
                bitIndex++;
            }
        }
    }
    /**
     * 将 bit 数组组建为对应的 byte
     * @param bitArray
     * @return
     */
    private byte bit2byte(byte[] bitArray)
    {
        byte b = 0x00;
        for (int i = 0; i < 8; i++)
        {
            b |= (byte)(bitArray[i] << (7 - i));
        }
        return b;
    }
    /**
     * 将给定的 bit 数组，按照每 8bit组成一个 byte 方式生成对应的 byte 数组。
     * @param byteArray
     * @param bitArray
     * @param nbytes
     */
    private void bit2byte(byte[] byteArray, byte[] bitArray, int nbytes)
    {
        for (int i = 0; i < nbytes; i++)
        {
            byteArray[i] = 0x00;
            for (int j = 0; j < 8; j++)
            {
                byteArray[i] |= (byte)(bitArray[j] << (7 - j));
            }
        }
    }

    public void setKey(char[] ByteKey, byte[] BitKey)
    {
        //用来保存 10 bit 的密钥信息
        byte[] ori_10_key_bit = new byte[16];
        //用来保存进行 P_10 处理以后的密钥信息
        byte[] p_10_key_bit = new byte[16];
        
        //因为总共只需要 10 个 bit，因此
        //对于第二个 byte 只获取最开始的两个 bit。
        ByteKey[1] &= 0xC0; // 16 bits->10 bits 
        
        //获取 10 个 bit 信息保存到 ori_10_bit
        byte2bit(ByteKey, ori_10_key_bit, 2);
        
        //对密钥进行 P_10 处理
        for (int i = 0; i < 10; i++)
            p_10_key_bit[i] = ori_10_key_bit[P_10[i] - 1];

        //在 P_10 的基础上执行 LS1：分为高位和低位。0-4为高位，执行左循环；5-9为低位，执行左循环
        for (int i = 0; i < 5; i++)
        {
            //P_10 的 0,1,2,3,4 变成 1,2,3,4,0
            ori_10_key_bit[(i + 4) % 5] = p_10_key_bit[i]; // Highbits Left rotate 1
            //P_10 的 5,6,7,8,9 变成 6,7,8,9,5
            ori_10_key_bit[(i + 4) % 5 + 5] = p_10_key_bit[i + 5]; // Lowbits Left rotate 1 
        }

        //LS1 的结果进行 P_8 处理，生成对应的密钥
        for (int i = 0; i < 8; i++)
            BitKey[i] = ori_10_key_bit[P_8[i] - 1];

        //在 LS1 的基础上执行 LS2：分为高位和低位。0-4为高位，执行左循环；5-9为低位，执行左循环
        for (int i = 0; i < 5; i++)
        {
            //LS1 后的 0,1,2,3,4 变成 2,3,4,0,1
            p_10_key_bit[(i + 3) % 5] = ori_10_key_bit[i]; // Highbits Left rotate 2 
            //LS1 后的 5,6,7,8,9 变成 7,8,9,5,6
            p_10_key_bit[(i + 3) % 5 + 5] = ori_10_key_bit[i + 5]; // Lowbits Left rotate 2 
        }

        //LS2 的结果进行 P_8 处理，生成对应的密钥
        for (int i = 0; i < 8; i++)
            BitKey[i + 8] = p_10_key_bit[P_8[i] - 1];
    }

    private char crypt(char sourceByte, byte[] bitKey, byte mode)
    {
        // mode = ENCRYPT or DECRTYP 
        byte[] temp = new byte[8];
        byte rs0, rs1;
        // byte->bits
        // 获取 byte 对应的 bit 信息
        byte2bit(sourceByte, bPlain);
        // 进行 IP 置换
        for (int i = 0; i < 8; i++)
            temp[i] = bPlain[IP[i] - 1];

        // F 
        for (int i = 0; i < 8; i++)
            bPlain[i] = (byte)(temp[E_P[i] - 1 + 4] ^ bitKey[i + mode * 8]);
        // S盒子：将 4 bit 的输入转换为 2 bit 的输出

        // S盒子：S0
        rs0 = S0[(bPlain[0] << 1 | bPlain[3])][(bPlain[1] << 1 | bPlain[2])];
        // S盒子：S1
        rs1 = S1[(bPlain[4] << 1 | bPlain[7])][(bPlain[5] << 1 | bPlain[6])];
        // 将 S0 盒子和 S1 盒子的结果进行合并，成为 4 bit。
        rs0 = (byte)(rs0 << 2 | rs1);

        //
        byte2bit((char)rs0, bPlain);

        for (int i = 0; i < 4; i++)
            temp[i] = (byte)(temp[i] ^ bPlain[P_4[i] - 1 + 4]);

        // SW 
        for (int i = 0; i < 8; i++)
            bPlain[i] = temp[(i + 4) % 8];
        for (int i = 0; i < 8; i++)
            temp[i] = bPlain[i];

        // F 
        for (int i = 0; i < 8; i++)
            bPlain[i] = (byte)(temp[E_P[i] - 1 + 4] ^ bitKey[i + (1 - mode) * 8]);
        rs0 = S0[(bPlain[0] << 1 | bPlain[3])][(bPlain[1] << 1 | bPlain[2])];
        rs1 = S1[(bPlain[4] << 1 | bPlain[7])][(bPlain[5] << 1 | bPlain[6])];
        rs0 = (byte)(rs0 << 2 | rs1);
        byte2bit((char)rs0, bPlain);
        for (int i = 0; i < 4; i++)
            temp[i] = (byte)(temp[i] ^ bPlain[P_4[i] - 1 + 4]);
        
        // IP-1 
        for (int i = 0; i < 8; i++)
            bPlain[i] = temp[FP[i] - 1];

        byte resultByte = bit2byte(bPlain);
        return byte2Char(resultByte);
    }

    public long getAmount(long amount, byte[] bitKey, byte mode)
    {
        char[] sourceBytes = longToBytes(amount, BYTE_COUNT);
        char[] destBytes = new char[sourceBytes.length];
        if (mode == ENCRYPT)
        {
            sourceBytes = swapEncrypt(sourceBytes);
        }

        for (int i = 0; i < 5; i++)
        {
            destBytes[i] = crypt(sourceBytes[i], bitKey, mode);
        }
        
        if (mode == DECRYPT)
        {
            destBytes = swapDecrypt(destBytes);
        }

        return bytesToLong(destBytes, BYTE_COUNT);
    }

    public long getEncryptAmount(long amount, byte[] bitKey)
    {
        return getAmount(amount, bitKey, ENCRYPT);
    }
    public long getDecryptAmount(long amount, byte[] bitKey)
    {
        return getAmount(amount, bitKey, DECRYPT);
    }
    /**
     * 加密时进行位移
     * @param sourceBytes
     * @return
     */
    private char[] swapEncrypt(char[] sourceBytes)
    {
        //5 byte 进行交换。
    	char[] result = new char[sourceBytes.length];
        result[4] = sourceBytes[0];
        result[3] = sourceBytes[1];
        result[2] = sourceBytes[2];
        result[0] = sourceBytes[3];
        result[1] = sourceBytes[4];
        return result;
    }
    /**
     * 解密时进行位移
     * @param sourceBytes
     * @return
     */
    private char[] swapDecrypt(char[] sourceBytes)
    {
        //5 byte 进行交换。
    	char[] result = new char[sourceBytes.length];
        result[3] = sourceBytes[0];
        result[4] = sourceBytes[1];
        result[2] = sourceBytes[2];
        result[1] = sourceBytes[3];
        result[0] = sourceBytes[4];
        return result;
    }

    private long bytesToLong(char[] sourceBytes, int byteCount)
    {
    	long result = 0;
    	for(char sourceByte : sourceBytes)
    	{
    		result = (result << 8);
    		result |= sourceByte;
    	}
    	return result;
    }
    
    private char[] longToBytes(long sourceValue, int byteCount)
    {
    	char[] resultBytes = new char[byteCount];
    	for(int i=0;i<byteCount;i++)
    	{
    		// 每次都是获取位移以后的最后一个 byte
    		char destByte = (char)((sourceValue >> 8*i) & 0xff);
    		// 获取到的 byte 保存到字节数组。
    		// 保存顺序从后到前
    		resultBytes[byteCount - 1 - i] = destByte;
    	}
    	return resultBytes;
    }
    
	private static char byte2Char(byte sourceByte)
	{
		return (char)(sourceByte & 0xff);
	}
	
	public static void simpleDESTest() throws Exception
	{
		SimpleDESHelper_Char helepr = new SimpleDESHelper_Char();
		char[] byteKey = new char[]{223,112};
		byte[] bitKey = new byte[16];
		helepr.setKey(byteKey, bitKey);
		long sourceAmount = 135673277688L;
		long encryptedAmount = helepr.getAmount(sourceAmount, bitKey, SimpleDESHelper_Char.ENCRYPT);
		System.out.println("加密结果:" + encryptedAmount);
		long decryptedAmount = helepr.getAmount(encryptedAmount, bitKey, SimpleDESHelper_Char.DECRYPT);
		System.out.println("解密结果:" + decryptedAmount);
	}
}
